package com.wn.sk.product.core.job;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wn.sk.product.core.contant.RedisContents;
import com.wn.sk.product.core.entity.PromotionSeckill;
import com.wn.sk.product.core.entity.PromotionStatus;
import com.wn.sk.product.core.vo.SeckillVo;
import com.wn.sk.product.core.service.IProductService;
import com.wn.sk.product.core.service.IPromotionSeckillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author yellow Docter
 * @date 2022 -09-05
 * @desc
 *  编写任务调度的类
 */
@Component
@Slf4j
public class ProductJob {

    @Autowired
    private IProductService productService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IPromotionSeckillService promotionSeckillService;

    /**
     *  设置任务调度的时间
     *
     *  需求:   5 秒钟执行一次方法
     *  1. cron 任务调用表达式:    秒 分钟 小时  日 月 周
     *  2. 注意: 日期和周 必须有一个是？,不确定
     *  3.例子:
     *  cron = "0/5 * * * * ?"   每5秒钟执行一次
     *  语法 cron 语法 确实很难, 百度
     *
     *  预热的时间：
     *  01 活动是 10:00:00  到底是每天还是每周了?
     *            09:59:00 提前一分钟预热 ？
     *            09:59:55 提前5秒钟预热 ？
     *
     *   方便演示:每5秒中查询一次
     *   通过表达式：
     *     0/5 * * * * ?  给指定的事件加载数据
     */
    @Scheduled(cron = "* * 1 * * ?")
    public void execute(){

        //01 查询秒杀商品,秒杀为开启的 查询查询到存储到redis
        List<SeckillVo> seckillVos = productService.findSeckillByStatus(PromotionStatus.UN_START);
        //02 存储到redis中 修改 数据库中的 商品的秒杀状态
        log.info("查询到的商品数据量:{},",seckillVos.size());
        //判断
        if (!CollectionUtils.isEmpty(seckillVos)){

            //修改后存储到redis中还是先存储到redis中 (秒杀状态没有什么作用 看前端的页面的展示而决定 )

            // 1.把数据存储到 redis中
           // 通过 key - obj的方式存储
           // opsForValue() 操作的是字符串
//           redisTemplate.opsForValue().set(RedisContents.PRODUCT_KILL_LIST,seckillVos);
            // 通过key  - arr 进行存储
           // 下面操作从是数组 .opsForList()  直接添加数组

            redisTemplate.opsForList().leftPushAll(RedisContents.PRODUCT_KILL_LIST,seckillVos);

            // 2. 修改 秒杀商品的状态
            // 2.1  秒杀商品的 id
            List<Integer> ids = seckillVos.stream()     // 获取流
                    .map(vo->vo.getSkId())              //把SeckillVo这个转成 Id
                    .collect(Collectors.toList());      //进行流的收集操作
            // 2.3  根据id 修改秒杀活动的状态
            // update pms_promotion_seckill set status =1 where id in (1,2,3);
            // update pms_promotion_seckill set status =1 where id in (ids);

            //修改条件对象
//            UpdateWrapper<PromotionSeckill> updateWrapper = new UpdateWrapper<>();
//            //设置字段
//            updateWrapper.set("status",PromotionStatus.START);
//            updateWrapper.in("id",ids);

            //把秒杀商品的 库存存储到redis中
            initRedis(seckillVos);

            //LambdaUpdateWrapper的更新写法
            //通过Wrappers 工具类型 来获取
            log.info("需要修改的id的状态:{}",ids);
            LambdaUpdateWrapper<PromotionSeckill> updateWrapper = Wrappers.lambdaUpdate(PromotionSeckill.class);
            //设置字段：  updateWrapper.set("status",PromotionStatus.START);
            // lambda的写法
            updateWrapper.set(PromotionSeckill::getStatus,PromotionStatus.START);
            //  updateWrapper.in("id",ids);
            updateWrapper.in(PromotionSeckill::getId,ids);


            //更新操作
            promotionSeckillService.update(updateWrapper);

        }

    }

    /**
     * 初始化秒杀商品的库存
     *
     *  sckill:count:1    [871,871,871,871,871]
     *
     *  sckill:count:活动商品的id     数组:[productId]  这个字段seckillCount决定
     *
     * @param seckillVos
     */
    private void initRedis(List<SeckillVo> seckillVos) {

        seckillVos.forEach(vo->{
            //活动id
            Integer skId = vo.getSkId();
            //商品id
            Integer pid = vo.getPid();
            //商品的库存
            Integer seckillCount = vo.getSeckillCount();
            for (int i = 0; i < seckillCount; i++) {
                redisTemplate.opsForList().leftPush(RedisContents.PRODUCT_KILL_COUNT + skId,pid);
            }
        });
    }

}
